在大数据领域这个聚类算法真是起到了十分重要的作用,只有通过有效地聚类才能得到非常直观的结果。
有一个实验要求对比两种大数据聚类算法的性能,具体的代码也不是由我实现的,我只是改了一部分,主要还是博客大佬们的代码,我这里借用了一下~~
具体的实验报告和python源码文件在最后位置,提供百度云下载,本文使用的是K-means算法和层次聚类算法AGNES,原理介绍和实验结果详见百度云提供的报告等
如今大数据的时代,大量的可以获得的数据被保存,用来分析以获得有用的信息,然而数据纷繁杂乱,需要研究者对齐进行一定的划分,将相似的数据放在一起进行分析,这就是所谓的聚类。
聚类就是按照某个特定标准(如距离准则,即数据点之间的距离)把一个数据集分割成不同的类或簇,使得同一个簇内的数据对象的相似性尽可能大,同时不在同一个簇中的数据对象的差异性也尽可能地大,常用的基本的距离计算方式就是欧氏距离。我们可以具体地理解为,聚类后同一类的数据尽可能聚集到一起,不同类数据尽量分离。聚类技术正在蓬勃发展,应用到的领域包括数据挖掘、统计学、机器学习、空间数据库技术、生物学以及市场营销等。各种聚类方法也被不断提出和改进,而不同的方法适合于不同类型的数据,因此对各种聚类方法、聚类效果的比较是一个值得研究的问题。
目前,有大量的聚类算法。而对于具体应用,聚类算法的选择取决于数据的类型、聚类的目的。如果聚类分析被用作描述或探查的工具,可以对同样的数据尝试多种算法,以发现数据可能揭示的结果。主要的聚类算法可以划分为如下几类:划分方法、层次方法、基于密度的方法、基于网格的方法以及基于模型的方法。
K-means聚类算法
K-means是划分方法中较经典的聚类算法之一。由于该算法的效率高,所以在对大规模数据进行聚类时被广泛应用。目前,许多算法均围绕着该算法进行扩展和改进。K-means算法目标是,以k为参数,把n个对象分成k个簇,使簇内具有较高的相似度,而簇间的相似度较低。
K-means算法的处理过程如下:首先,随机地 选择k个对象,每个对象初始地代表了一个簇的平均值或中心;对剩余的每个对象,根据其与各簇中心的距离,将它赋给最近的簇;然后重新计算每个簇的平均值。 这个过程不断重复,直到准则函数收敛。
通常,采用平方误差准则。平方误差准则采用的是数据库中所有对象的平方误差的总和,与是空间中的点和每个簇的的平均值有关[9]。目标是使生成的簇尽可能紧凑独立,使用的距离度量是欧几里得距离,当然也可以用其他距离度量。K-means聚类算法的算法流程如下:
输入:包含n个对象的数据库和簇的数目k;
输出:k个簇,使平方误差准则最小。
步骤:
(1) 任意选择k个对象作为初始的簇中心;
(2) repeat;
(3) 根据簇中对象的平均值,将每个对象(重新)赋予最类似的簇;
(4) 更新簇的平均值,即计算每个簇中对象的平均值;
(5) until不再发生变化。
优点:简单直接(体现在逻辑思路以及实现难度上),易于理解,在低维数据集上有不错的效果(简单的算法不见得就不能得到优秀的效果)。
缺点:对于高维数据(如成百上千维,现实中还不止这么多),其计算速度十分慢,主要是慢在计算距离上(参考欧几里得距离,当然并行化处理是可以的,这是算法实现层面的问题),它的另外一个缺点就是它需要我们设定希望得到的聚类数k,若我们对于数据没有很好的理解,那么设置k值就成了一种估计性的工作,并且会对实验结果造成很大的偏差。
层次聚类算法
根据层次分解的顺序是自底向上的还是自上向下的,层次聚类算法分为凝聚的层次聚类算法和分裂的层次聚类算法。
凝聚型层次聚类的策略是先将每个对象作为一个簇,然后合并这些原子簇为越来越大的簇,直到所有对象都在一个簇中,或者某个终结条件被满足。绝大多数层次聚类属于凝聚型层次聚类,它们只是在簇间相似度的定义上有所不同。四种广泛采用的簇间距离度量方法为:最小距离,最大距离,平均值距离,平均距离。
这里以最小距离的凝聚层次聚类算法流程为例:
(1) 将每个对象看作一类,计算两两之间的最小距离;
(2) 将距离最小的两个类合并成一个新类;
(3) 重新计算新类与所有类之间的距离;
(4) 重复(2)、(3),直到所有类最后合并成一类。
优点:
- 距离和规则的相似度容易定义,限制少;
- 不需要预先制定聚类数;
- 可以发现类的层次关系(在一些特定领域如生物有很大作用);
缺点: - 计算复杂度太高(考虑并行化);
- 奇异值也能产生很大影响;
- 算法很可能聚类成链状(一层包含着一层);
- 算法不需要预定聚类数,但是我们选择哪个层次的聚类作为我们需要的聚类效果,这需要我们按照实际客观情况以及经验来完成,毕竟就凝聚聚类来说,从最底层的每个个体作为一个个体,到最顶层所有个体合并为一个个体,其中的聚类结果可能有许许多多种。当然针对这个问题也有许多解决方案,其中一个常用的就是凝聚到某个程度其聚类之间的距离都大于某个阈值k,就停止凝聚。
具体代码
在实验中,本人先是下载的点集,然后为了测试效率写了一个简单的产生大量点的脚本,具体的实现代码如下(来自网络 进行了小小的更改),注意需要的库文件,没有的安装一下,还有路径也需要改:
AGNES
#-*- coding:utf-8 -*-
import math
import pylab as pl
import codecs
import re
import datetime
pl.rcParams['axes.unicode_minus'] = False
# 为了显示汉字坐标的
#计算欧式距离,a,b代表两个元组
def calcudistance(a, b):
return math.sqrt(math.pow(a[0]-b[0], 2)+math.pow(a[1]-b[1], 2))
# 求出最小距离
def dist_min(Ci, Cj):
return min(calcudistance(i, j) for i in Ci for j in Cj)
# 求出最大距离
def dist_max(Ci, Cj):
return max(calcudistance(i, j) for i in Ci for j in Cj)
#求出平均距离
def dist_avg(Ci, Cj):
return sum(calcudistance(i, j) for i in Ci for j in Cj)/(len(Ci)*len(Cj))
#找到距离最小的下标
def find_Min(M):
min = 1000
x = 0; y = 0
for i in range(len(M)):
for j in range(len(M[i])):
if i != j and M[i][j] < min:
min = M[i][j];x = i; y = j
return (x, y, min)
#算法核心
def AGNES(dataset, dist, k):
#初始化C和M
C = [];M = []
for i in dataset:
Ci = []
Ci.append(i)
C.append(Ci)
for i in C:
Mi = []
for j in C:
Mi.append(dist(i, j))
M.append(Mi)
q = len(dataset)
#合并更新
while q > k:
x, y, min = find_Min(M)
C[x].extend(C[y])
C.remove(C[y])
M = []
for i in C:
Mi = []
for j in C:
Mi.append(dist(i, j))
M.append(Mi)
q -= 1
return C
# 画出结果图
def drawfig(C):
colValue = ['r', 'y', 'g', 'b', 'c', 'k', 'm'] # 颜色数组
for i in range(len(C)):
coo_X = [] # x坐标
coo_Y = [] # y坐标
for j in range(len(C[i])):
coo_X.append(C[i][j][0])
coo_Y.append(C[i][j][1])
pl.scatter(coo_X, coo_Y, marker='o', color=colValue[i%len(colValue)], label=i)
pl.legend(loc='upper right')
pl.title("聚类结果图")
pl.savefig(savepath + '2.png')
pl.show()
def draworigian(dataset):
x_list = list()
y_list = list()
for i in range(len(dataSet)):
temp = dataSet[i]
x_list.append(temp[0])
y_list.append(temp[1])
pl.scatter(x_list, y_list, marker='o', color="b")
pl.legend(loc='upper right')
pl.title("数据原始分布")
pl.savefig(savepath + '1.png')
pl.show()
def loadtxt(Filepath):
# 读取文本 保存为二维点集
inDate = codecs.open(Filepath, 'r', 'utf-8').readlines()
dataSet = list()
for line in inDate: # 段落的处理
line = line.strip()
strList = re.split('[ ]+', line)
numList = list()
for item in strList:
num = float(item)
numList.append(num)
# print numList
dataSet.append(numList)
return dataSet # dataSet = [[], [], [], ...]
savepath='D:/学习/研1/模式识别/AGNES/'
Filepath = "D:/学习/研1/模式识别/testSet.txt" # 数据集文件
dataSet = loadtxt(Filepath) # 载入数据集
draworigian(dataSet)
start = datetime.datetime.now()
result = AGNES(dataSet, dist_avg, 4)
end = datetime.datetime.now()
timeused = end - start
print(timeused)
drawfig(result)
# 100 1.203133, 01.140652, 1.156260, 1.203152, 1.453138
# 200点 9.359476, 09.367193, 09.312600, 09.325362, 09.356845
# 500点 147.946446, 147:351248, 147.153595,147.946446, 145.493638
# 500 无需 145.429797 146.016936 147.240645 146.563253 147.534587
k-MEANS
# -*- coding: UTF-8 -*-
import numpy
import random
import codecs
import re
import matplotlib.pyplot as plt
import datetime
plt.rcParams['axes.unicode_minus'] = False # 显示负号
# 计算欧式距离,a,b代表两个向量
def calcudistance(a,b):
return numpy.sqrt(numpy.sum(numpy.square(a - b)))
# 初始化k个质心,数据集中随机选择
def initcentroids(dataSet, k):
return random.sample(dataSet, k) # 从dataSet中随机获取k个数据项返回
def mindistance(dataSet, centroidList):
# 对每个属于dataSet的item,计算item与centroidList中k个质心的欧式距离,找出距离最小的,
# 并将item加入相应的簇类中
clusterDict = dict() # 用dict来保存簇类结果
for item in dataSet:
vec1 = numpy.array(item) # 转换成array形式
flag = 0 # 簇分类标记,记录与相应簇距离最近的那个簇
minDis = float("inf") # 初始化为最大值
for i in range(len(centroidList)):
vec2 = numpy.array(centroidList[i])
distance = calcudistance(vec1, vec2) # 计算相应的欧式距离
if distance < minDis:
minDis = distance
flag = i # 循环结束时,flag保存的是与当前item距离最近的那个簇标记
if flag not in clusterDict.keys(): # 簇标记不存在,进行初始化
clusterDict[flag] = list()
# print flag, item
clusterDict[flag].append(item) # 加入相应的类别中
return clusterDict # 返回新的聚类结果
def getCentroids(clusterDict):
# 得到k个质心
centroidList = list()
for key in clusterDict.keys():
centroid = numpy.mean(numpy.array(clusterDict[key]), axis=0) # 计算每列的均值,即找到质心
# print key, centroid
centroidList.append(centroid)
return numpy.array(centroidList).tolist()
def getVar(clusterDict, centroidList):
# 计算簇集合间的均方误差
# 将簇类中各个向量与质心的距离进行累加求和
sum = 0.0
for key in clusterDict.keys():
vec1 = numpy.array(centroidList[key])
distance = 0.0
for item in clusterDict[key]:
vec2 = numpy.array(item)
distance += calcudistance(vec1, vec2)
sum += distance
return sum
# 画出结果他图
def drawfig(centroidList, clusterDict):
# 展示聚类结果
global imgcount
imgcount += 1
colorMark = ['or', 'ob', 'og', 'ok', 'oy', 'ow'] # 不同簇类的标记 'or' --> 'o'代表圆,'r'代表red,'b':blue
centroidMark = ['dr', 'db', 'dg', 'dk', 'dy', 'dw'] # 质心标记 同上'd'代表棱形
for key in clusterDict.keys():
plt.plot(centroidList[key][0], centroidList[key][1], centroidMark[key], markersize=12) # 画质心点
for item in clusterDict[key]:
plt.plot(item[0], item[1], colorMark[key]) # 画簇类下的点
plt.title("聚类分布图")
plt.savefig(savepath + str(imgcount) + '.png')
plt.show()
# 关键部分
def k_means(dataset,kindnum):
centroidList = initcentroids(dataSet, 4) # 初始化质心,设置k=4
clusterDict = mindistance(dataSet, centroidList) # 第一次聚类迭代
newVar = getVar(clusterDict, centroidList) # 获得均方误差值,通过新旧均方误差来获得迭代终止条件
oldVar = -0.0001 # 旧均方误差值初始化为-1
print("----- 第1次迭代 -----")
print('k个均值向量: ')
print(centroidList)
print('平均均方误差: %d' % (newVar))
# drawfig(centroidList, clusterDict) # 展示聚类结果
k = 2
while abs(newVar - oldVar) >= 0.0001: # 当连续两次聚类结果小于0.0001时,迭代结束
centroidList = getCentroids(clusterDict) # 获得新的质心
clusterDict = mindistance(dataSet, centroidList) # 新的聚类结果
oldVar = newVar
newVar = getVar(clusterDict, centroidList)
print('----- 第%d次迭代 -----\n簇类' %(k))
print('k个均值向量: ')
print(centroidList)
print('平均均方误差: %d' %(newVar))
k += 1 # 迭代次数
return (centroidList,clusterDict)
def loadtxt(Filepath):
# 读取文本 保存为二维点集
inDate = codecs.open(Filepath, 'r', 'utf-8').readlines()
dataSet = list()
for line in inDate:
line = line.strip()
strList = re.split('[ ]+', line) # 去除多余的空格
# print strList[0], strList[1]
numList = list()
for item in strList:
num = float(item)
numList.append(num)
# print numList
dataSet.append(numList)
return dataSet # dataSet = [[], [], [], ...]
savepath = 'D:/学习/研1/模式识别/K-means/'
imgcount = 0
Filepath = "D:/学习/研1/模式识别/testSet.txt" # 数据集文件
dataSet = loadtxt(Filepath) # 载入数据集
start = datetime.datetime.now()
centroidList, clusterDict = k_means(dataSet, 4)
end = datetime.datetime.now()
timeused = end - start
print(timeused)
drawfig(centroidList, clusterDict) # 展示聚类结果
# 100 0.031245, 0.015623, 0.031249, 0.015609, 0.015624
# 200点 0.031232, 0.031253, 0.046892, 0.031234, 0.046875
# 500点 0.156265, 0.093733, 0.078108,0.062499, 0.187502
# 10000 2.000017
# 无顺序 500 00.218750 00.547491 00.421866 00.281266 00.281266
最后这是是一个产生点集的脚本,分两种,一个是无序的随机,另一个是大概扎堆的,一个是为了检测效率,一个是为了测试效果。
import numpy as np
import copy
choosetype = 2 # 1 表示有序点 其他表示随机点
data = [[3.5, -3.5], [3.5, 3.5], [-3.5, 3.5], [-3.5, -3.5]] # 四类点的中心
totalnum =500 #产生点的个数
file_path = "D:\学习\研1\模式识别\\testSet2.txt" # 保存路径
fopen = open(file_path, 'w') # 追加的方式读写打开
for i in range(totalnum):
if choosetype == 1:
datatemp = copy.deepcopy(data)
choose = datatemp[i % 4]
n1 = 2*np.random.random(1) - 1
n2 = 2*np.random.random(1) - 1
choose[0] = choose[0] + n1
choose[1] = choose[1] + n2
fopen.writelines(str(round(choose[0][0], 6)) + " " + str(round(choose[1][0], 6)) + "\n")
else:
n1 = 4 * np.random.random(1) - 2
n2 = 4 * np.random.random(1) - 2
fopen.writelines(str(round(n1[0], 6)) + " " + str(round(n2[0], 6)) + "\n")
fopen.close()
实验结果
k-means测试结果,该算法有效
AGNES的结果,算法有效
为了比较性能,对大量的点进行实验,表格如下:
具体的算法原理和实验分析见报告内容,最后面提供百度云源码和报告的连接
心得体会
大数据可以说是现在研究十分火热的一个课题,在众多的研究室和科技公司等都是属于一个较为核心的项目。尤其是在当今网络迅速发展的时代,信息就意味着资源,如果能掌握信息就能把握机会。作为大数据分析的关键环节,聚类为分析者提供了效果超凡的数据预处理,使得我们可以发现在数据之下隐藏的逻辑关系与形势。
在众多的聚类算法之中,我们对两个基本的算法K-means和AGNES进行了了解和学习,并实用python语言来实现和对比两者的效果。结果表明,从大数据量的运算来讲,K-means快很多,但AGNES对数据的适用性更高。
在模式识别课程上,学习到了很多有关的人工智能和大数据分析等的知识,可以说获益匪浅。经过动手做实验,更加加深了我对聚类方法的理解,有助于以后更加深入的学习。首先,我要感谢老师耐心细致的授课,对基本概念的讲解十分简明易懂,采用的方式也让人容易理解。其次,感谢我的组员同学们对我实验期间的帮助,共同进步,共同提高。
下载地址:
链接: https://pan.baidu.com/s/1r0MXJSKO5uKq4hWa5IL0Dw 密码: 6sd3